﻿#pragma once

#include <cstdint>
#include <functional>
#include <list>
#include <string>
#include <unordered_map>
#include <vector>

#ifndef DISABLE_SB_CODE
#include "util/box_std_allocator.hh"
#endif

namespace kratos {
namespace service {

class ServiceBox;
class ServiceFinder;
class ServiceRegister;
class BoxNetwork;

/**
 * 容器地址信息
 */
struct Host {
  std::string host;           ///< 容器地址
  std::uint64_t channel_id;   ///< 管道
  std::uint64_t ref_count{0}; ///< 被引用的次数
};

/**
 * 服务层事件
 */
enum class ServiceLayerEvent {
  CONNECT = 1,
  CLOSE,
};

/**
 * 连接事件回调
 */
using ServiceEventCallback = std::function<void(
    const std::string &, std::uint64_t, std::uint32_t, ServiceLayerEvent)>;

/**
 * 容器信息
 */
struct HostInfo {
#ifndef DISABLE_SB_CODE
  kratos::service::PoolVector<Host> host_vec; ///< 容器地址信息数组
#else
  std::vector<Host> host_vec; ///< 容器地址信息数组
#endif
  std::size_t index; ///< 轮询索引
};

/**
 * 服务层，维护服务到其他容器的管道，按需建立连接
 */
class ServiceLayer {
  // 回调信息
  struct CbInfo {
    ServiceEventCallback cb;
    bool trigger{false};
    std::uint32_t cb_id{0};
  };
#ifndef DISABLE_SB_CODE
  using ServiceMap = kratos::service::PoolUnorederedMap<std::string, HostInfo>;
  using CbMap =
      kratos::service::PoolUnorederedMap<std::string,
                                         kratos::service::PoolList<CbInfo>>;
  using ConnctedHostMap = kratos::service::PoolUnorederedMap<
      std::string, kratos::service::PoolList<std::string>>;

#else
  using ServiceMap = std::unordered_map<std::string, HostInfo>;
  using CbMap = std::unordered_map<std::string, std::list<CbInfo>>;
  using ConnctedHostMap =
      std::unordered_map<std::string, std::list<std::string>>;
#endif

  ServiceMap service_map_;       ///< 已建立连接的服务表
  ServiceBox *box_{nullptr};     ///< 服务容器
  BoxNetwork *network_{nullptr}; ///< 网络
  CbMap cb_map_;                 ///< 回调表, {service name, cb list}
  ServiceFinder *service_finder_{nullptr};     ///< 服务发现
  ServiceRegister *service_register_{nullptr}; ///< 服务注册
  ConnctedHostMap connecting_host_map_; ///< {host, the list of service name}
                                        ///< 正在连接中的地址
  std::string balance_type_{"random"};  ///< 负载均衡方式

public:
  /**
   * 构造
   *
   * \param box 服务容器
   */
  ServiceLayer(ServiceBox *box);
  /**
   * 析构
   */
  ~ServiceLayer();
  /**
   * 设置服务发现和注册
   *
   * \param network 网络
   * \param service_finder 服务发现
   * \param service_register 服务注册
   */
  auto set_service_mach(BoxNetwork *network, ServiceFinder *service_finder,
                        ServiceRegister *service_register) -> void;
  /**
   * 当管道连接建立时调用
   *
   * \param name 服务名
   * \param channel_id 管道ID
   */
  auto on_connect(const std::string &name, std::uint64_t channel_id) -> void;
  /**
   * 当管道连接关闭时调用
   *
   * \param name 服务名
   * \param channel_id 管道ID
   */
  auto on_close(const std::string &name, std::uint64_t channel_id) -> void;
  /**
   * 从缓存内获取服务对应的管道，如果不存在则尝试建立到其他容器的连接并立即返回0
   *
   * \param service_name 服务名
   * \return channel_id 管道ID
   */
  auto get_channel(const std::string &service_name) -> std::uint64_t;
  /**
   * 管道是否有效.
   *
   * \param service_name 服务名称
   * \param channel_id 管道ID
   * \return
   */
  auto check_channel(const std::string &service_name, std::uint64_t channel_id)
      -> bool;
  /**
   * 从缓存内获取服务对应的管道，如果不存在则尝试建立到其他容器的连接并立即返回0,
   * 后续服务管道事件发生时，会回调cb
   *
   * \param service_name 服务名
   * \param cb 回调函数
   * \return cb_id 回调ID
   */
  auto get_channel(const std::string &service_name, ServiceEventCallback cb,
                   std::uint32_t cb_id) -> std::uint64_t;
  /**
   * 从缓存内获取服务对应的管道，如果不存在则立即返回0
   *
   * \param service_name 服务名
   * \return channel_id 管道ID
   */
  auto try_get_channel(const std::string &service_name) -> std::uint64_t;
  /**
   * 获取所有已经连接的服务
   */
  auto get_remote_service() -> const ServiceMap &;
  /**
   * 汇报失效管道
   *
   * \param channel_id 管道ID
   */
  auto report_bad_channel(std::uint64_t channel_id) -> void;
  /**
   * 获取真正的服务名
   *
   * \param service_name 服务名
   */
  inline static auto get_real_name(const std::string &service_name)
      -> std::string {
    std::string real_name;
    if (service_name[0] != '/') {
      real_name = "/" + service_name;
    } else {
      real_name = service_name;
    }
    return real_name;
  }
  /**
   * 设置负载均衡配置类型s
   */
  auto set_balance_type(const std::string &balance_type) -> void;

private:
  /**
   * 服务变化监听函数
   */
  auto service_listener(const std::string &name,
                        const std::vector<std::string> &hosts) -> void;
  /**
   * 连接到其他容器
   *
   * \param name 服务名
   * \param host 容器地址
   * \return true或false
   */
  auto connect_to_host(const std::string &name, const std::string &host)
      -> bool;
  /**
   * 已负载均衡的方式获取管道
   */
  auto get_channel_balance(HostInfo &host_info) -> std::uint64_t;
  /**
   * 已负载均衡的方式获取管道 - 轮训
   */
  auto get_channel_balance_round_robin(HostInfo &host_info) -> std::uint64_t;
  /**
   * 已负载均衡的方式获取管道 - 随机
   */
  auto get_channel_balance_random(HostInfo &host_info) -> std::uint64_t;
  /**
   * 已负载均衡的方式获取管道 - LRU
   */
  auto get_channel_balance_lru(HostInfo &host_info) -> std::uint64_t;
};

} // namespace service
} // namespace kratos
